iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
Security

你的程式真的安全嗎?從資安的角度做 code review系列 第 8

C - SQL injection(注入缺陷:SQL 注入)

  • 分享至 

  • xImage
  •  

下方程式碼片段全部都是擷取自 Secure Code Warrior 線上安全程式培訓平台,因為練習互動時的題目多半不會只有單一個檔案,可能涉及多個檔案、資料夾及多處地方修改,因此我的文章主要是針對最主要的區塊做修改及說明,若有不好理解的地方非常抱歉也還請見諒,也可以實際上去 Secure Code Warrior 玩玩看,搭配著互動,會更有感的學習哦~

C - SQL injection(注入缺陷:SQL 注入)

  • 形成原因:當應用程式在使用它的動態構建語句之前未能對輸入進行消毒或驗證時。
  • 後果:攻擊者存取資料庫,並在未經許可的情況下檢視或修改資料。
  • 實例:輸入跳脫邏輯判斷,導致之後不管輸入什麼都會是true。
SELECT * FROM Users WHERE Username = 'admin' AND Password = 'abc' OR 1=1;--
  • 解決方法:
    • 使用參數化查詢
      • 不正確的程式碼:
      insert_user_query = "INSERT INTO users(name, age)"
      
      VALUES (request_user_name + "," + request_user_age + ")";
      
      insert_user = db.preapare(insert_user_query)
      
      insert_user.execute() 
      //程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台
      
      • 正確的程式碼:
      insert_user = db.prepare "INSERT INTO user(name,age)VALUES(?, ?)"
      
      insert_user.execure
      
      (request_user_name, request_user_age)
      //程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台
      
    • 事先設立允許輸入清單過濾垃圾字元
    • 針對每一個資料庫管理員設定最小管理權限
    • 使用 GET 或 POST method 、Cookie和其他 HTTP method。

第1題

錯誤區塊

std::string query = "INSERT INTO users( name, login, password ) VALUES(" + account->getValues() + ")";
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

std::string query = "SELECT COUNT(*) FROM users WHERE login = '" + account->login + "'";
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
只需將使用者登入指定為以下內容即可抹掉所有資訊:;DELETE FROM users WHERE login LIKE %'
以下SQL query,將清除“使用者”表:SELECT COUNT(*) FROM users WHERE login = '';DELETE FROM users WHERE login LIKE '%';

主要修正方法

Database.cpp

std::string query = "INSERT INTO users( name, login, password ) VALUES(" + account->getValues() + ")";
result = PQexec( connection, query.c_str() );
if( PQresultStatus( result ) != PGRES_COMMAND_OK )
{
    error( query, result );
    return;
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

改成

DBQuery dbQuery = DBQuery("INSERT INTO users( name, login, password ) VALUES( $1::varchar, $2::varchar, $3::varchar )");
dbQuery.arg( account->name ).arg( account->login ).arg( account->password );
result = dbQuery.execute( connection );
if( PQresultStatus( result ) != PGRES_COMMAND_OK )
{
    error( dbQuery.get(), result );
    return;
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

DB.cpp

std::string query = "SELECT COUNT(*) FROM users WHERE login = '" +
                    account->login + "'";
PGresult* result = PQexec( connection, query.c_str() );
if( PQresultStatus( result ) != PGRES_TUPLES_OK )
{
    error( query, result );
    return false;
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

改成

DBQuery dbQuery("SELECT COUNT(*) FROM users WHERE login = $1::varchar");
dbQuery.arg( account->login );
PGresult* result = dbQuery.execute( connection );
if( PQresultStatus( result ) != PGRES_TUPLES_OK )
{
    error( dbQuery.get(), result );
    return false;
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
使用 prepared SQL語句更好,因為 prepared statements 將所有提交的使用者資料視為參數,這些參數內容不能修改 SQL request 邏輯以傷害資料庫,防止SQL注入攻擊。

第2題

錯誤區塊

std::string query = "SELECT * FROM users WHERE login = '" + login + "' AND password = '" + password +"'";
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
Account Database::getUser中的動態SQL查詢容易被SQL注入。因為沒有限制對使用者提供的傳入資料進行檢查或消毒。
例如,只需由使用者輸入以下內容即可獲得授權並存取系統:
login = "x' OR 'x' = 'x";
password = "y' OR 'y' = 'y";

主要修正方法

把原程式碼:

std::string query = "SELECT * FROM users WHERE login = '" + login + "' AND password = '" + password +"'";
PGresult* result = PQexec( connection, query.c_str() );

if( PQresultStatus( result ) != PGRES_TUPLES_OK )
{
    error( query, result );
    return account;
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

改成

DBQuery dbQuery("SELECT * FROM users WHERE login = $1::varchar AND password = $2::varchar");
dbQuery.arg( login ).arg( password );
PGresult* result = dbQuery.execute( connection );

if( PQresultStatus( result ) != PGRES_TUPLES_OK )
{
    error( dbQuery.get(), result );
    return account;
}
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
總是使用 prepared SQL 語句而不是直接將使用者輸入嵌入到SQL語法中

第3題

錯誤區塊

scriptfile << "@if not exist " + fullName + " echo " + NOTFOUND;
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
作為引數傳遞的值用於建立 batch 檔的內容,執行後允許攻擊者更改正在執行的程式語法,從而執行輸入的惡意程式,這不安全。此外,該程式僅適用於Windows作業系統。

主要修正方法

把原程式碼整段刪掉

const std::string NOTFOUND("!NOTFOUND!");
char buffer[128]{};
std::ofstream scriptfile("findMySit.bat");
scriptfile << "@if not exist " + fullName + " echo " + NOTFOUND;
scriptfile.close();
FILE* pipe = _popen("findMySit.bat", "r");
fgets(buffer, sizeof buffer, pipe);
_pclose(pipe);
if (std::equal(NOTFOUND.begin(), NOTFOUND.end(), std::begin(buffer)) == true)
    return false;
std::ifstream infile(fullName);
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

改成

std::ifstream infile(fullName);
if (infile.good() == false)
    return false;
//程式碼片段擷取自 Secure Code Warrior 線上安全程式培訓平台

解釋:
建議使用標準庫的功能,而不是根據輸入的值執行程式碼或可執行的 batch 檔。使用檔案系統庫中的good()函式,該函式執行所有必要的檢查,並透過傳遞的值阻止程式執行。


上一篇
C - stack buffer overflow(記憶體損壞:堆疊溢位)
下一篇
C - weak crypto algorithm (不安全的密碼術:演算法使用方式脆弱)
系列文
你的程式真的安全嗎?從資安的角度做 code review25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言